/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.core.base;

import org.proteusframework.core.api.IPlatformDelegate;
import org.proteusframework.core.api.IRuntimeConfiguration;
import org.proteusframework.core.api.IRuntimeEngine;
import org.proteusframework.core.api.RuntimeEnvironment;
import org.proteusframework.core.api.model.IGlyphRenderer;
import org.proteusframework.core.api.model.INamespace;
import org.proteusframework.core.runtime.DefaultRuntimeConfiguration;
import org.proteusframework.core.util.Assert;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Base class for all Proteus {@link IRuntimeEngine} implementations.
 *
 * @author Tacoma Four
 */
public abstract class AbstractRuntimeEngine implements IRuntimeEngine
{
    /**
     * Logger.
     */
    private static final Logger logger = Logger.getLogger(AbstractRuntimeEngine.class.getCanonicalName());

    /**
     * Runtime configuration that defines the behavior of the runtime engine.
     */
    protected final IRuntimeConfiguration runtimeConfiguration;

    /**
     * Platform delegate.
     */
    protected IPlatformDelegate delegate = null;

    /**
     * Flag that tracks if the engine is presently running.
     */
    protected boolean runningFlag = false;

    /**
     * Platform delegate interface.
     */
    protected Class<? extends IPlatformDelegate> delegateInterface;

    /**
     * Runtime environment.
     */
    protected final RuntimeEnvironment runtimeEnvironment;

    /**
     * Unique namespace for the application itself.
     */
    protected INamespace namespace;


    /**
     * Glyph renderer instance.
     */
    protected IGlyphRenderer glyphRenderer;

    /**
     * Constructs a runtime engine targeting the specified runtime environment, dynamically configured using a
     * {@link DefaultRuntimeConfiguration} that reads its configuration from a .properties file found on the CLASSPATH,
     * typically inside of the Proteus .jar file.
     *
     * @param runtimeEnvironment Target runtime environment
     * @param glyphRenderer Glyph renderer
     */
    protected AbstractRuntimeEngine(final RuntimeEnvironment runtimeEnvironment, IGlyphRenderer glyphRenderer)
    {
        this(runtimeEnvironment, glyphRenderer, new DefaultRuntimeConfiguration());
    }

    /**
     * Constructs a runtime engine targeting the specified runtime environment, dynamically configured from the provided
     * runtime configuration.
     *
     * @param runtimeEnvironment   Target runtime environment
     * @param glyphRenderer Glyph renderer
     * @param runtimeConfiguration Target runtime configuration
     */
    protected AbstractRuntimeEngine(final RuntimeEnvironment runtimeEnvironment,
                                    final IGlyphRenderer glyphRenderer,
                                    final IRuntimeConfiguration runtimeConfiguration)
    {
        Assert.parameterNotNull(runtimeEnvironment, "Parameter 'runtimeEnvironment' must not be null");
        Assert.parameterNotNull(glyphRenderer, "Parameter 'glyphRenderer' must not be null");
        Assert.parameterNotNull(runtimeConfiguration, "Parameter 'runtimeConfiguration' must not be null");

        if (logger.isLoggable(Level.FINEST))
        {
            logger.finest("Runtime Environment: " + runtimeEnvironment);
        }

        this.runtimeEnvironment = runtimeEnvironment;
        this.glyphRenderer = glyphRenderer;
        this.runtimeConfiguration = runtimeConfiguration;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final IRuntimeConfiguration getRuntimeConfiguration()
    {
        return runtimeConfiguration;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final RuntimeEnvironment getRuntimeEnvironment()
    {
        return runtimeEnvironment;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public IGlyphRenderer getGlyphRenderer()
    {
        return glyphRenderer;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final <T extends IPlatformDelegate> void setPlatformDelegate(IPlatformDelegate delegate,
                                                                        Class<T> delegateInterface)
    {
        Assert.unitialized(this.delegate, "Platform delegate is implicitly final and cannot be modified.");
        Assert.parameterNotNull(delegate, "Parameter 'delegate' must not be null");

        this.delegate = delegate;
        this.delegateInterface = delegateInterface;

        if (logger.isLoggable(Level.FINEST))
        {
            logger.finest("Passing platform delegate into IRuntimeConfiguration");
        }

        //
        // Always inject the platform delegate into the runtime configuration first
        this.runtimeConfiguration.setPlatformDelegate(delegate, delegateInterface);


        if (logger.isLoggable(Level.FINEST))
        {
            logger.finest("Passing platform delegate into this");
        }

        onDelegateInjected();
    }

    @Override
    public final void stop()
    {
        logger.info("RuntimeEngine is stopping...");
        runningFlag = false;
        onEngineStopped();
    }

    /**
     * Hook for extensions to respond to the engine stopping. By the time this event is fired, the engine's
     * {@link #runningFlag} has been set to false, indicating to the outside world that the engine is stopped,
     * but technically the engine isn't considered fully stopped until the completion of this event.
     */
    protected void onEngineStopped()
    {

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final boolean isRunning()
    {
        return runningFlag;
    }

    /**
     * Empty delegate injection event handler for descendant classes.
     */
    protected abstract void onDelegateInjected();
}
